package main

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"

	"github.com/gin-gonic/gin"
	"gopkg.in/yaml.v2"
)

//# 假设存在一个用户表，存放了用户信息。
//# 每个用户有以下字段
type VIPInfo struct {
	Username string   `yaml:",omitempty" json:"username,omitempty"`
	Password string   `yaml:",omitempty" json:"password,omitempty"`
	Nickname string   `yaml:",omitempty" json:"nickname,omitempty"`
	BirthDay int64    `yaml:",omitempty" json:"birthday,omitempty"`
	Interest []string `yaml:",omitempty" json:"interest,omitempty"`
}

//# 这些用户被组织成一个列表
type UsersData struct {
	Users []VIPInfo `yaml:",omitempty" json:"users,omitempty"`
}

var gUserData UsersData

func init() {
	var v VIPInfo
	v.Username = "testuser1"
	v.Password = "testpswd1"
	v.Nickname = "test"
	v.BirthDay = 1024000000
	v.Interest = append(v.Interest, "book")
	gUserData.Users = append(gUserData.Users, v)
}

//# 程序启动时将从配置文件中读取这些数据
//# 然后这个程序会提供一些接口，用于：
//# (1)取特定用户的所有信息
//# (2)修改用户信息
//# (3)添加新用户
//# (4)删除用户
func registerConfig(config *gin.RouterGroup) {
	// 例1：URL带参数和没有带参数的HTTP GET。
	// 返回JSON。
	// GET http://127.0.0.1:4210/someprefix/config
	// 返回所有用户信息
	config.GET("", getConfig)

	// 例2： BODY中有和没有带参数的HTTP POST；
	// 返回JSON；
	// Request的Content-Type: "application/x-www-form-urlencoded"。
	// POST http://127.0.0.1:4210/someprefix/config
	// 返回所有用户信息。
	config.POST("", postConfig)

	// 例3：使用HTTP PUT方法修改特定的数据
	// body为JSON；
	// 返回JSON；
	// Request的Content-Type: "application/json"
	// PUT http://127.0.0.1:4210/someprefix/config/user
	// 添加一个用户。
	config.PUT("/user", AddUser)

	// 例4：HTTP POST在路径中带参数，同时在Body中带（或不带）参数。
	// POST http://127.0.0.1:4210/someprefix/config/user/:name
	// query=item
	// 查询一个用户的特定字段，如果query参数为空则返回所有信息。返回值为json格式
	config.POST("/user/:username", QueryUser)
}

// Write 这个函数将当前配置以format参数指定的格式写入到一个io.Writer中（写文件，写缓冲区等）
func (c *UsersData) Write(w io.Writer, format string) error {
	switch format {
	case "json":
		enc := json.NewEncoder(w)
		enc.SetIndent("", "  ")
		enc.Encode(c)
		return nil
	case "yaml":
		fallthrough
	default:
		enc := yaml.NewEncoder(w)
		defer enc.Close()
		return enc.Encode(c)
	}
}

// 给客户端的标准回应。
type Response struct {
	Code int    `json:"code,omitempty"`
	Msg  string `json:"msg,omitempty"`
}

// 请求的格式
type getConfigRequest struct {
	Format string `form:"format" json:"format"`
}

// 返回值的格式
type getConfigResponse struct {
	data *UsersData
}

// HTTP GET 获取所有用户信息。
// curl http://127.0.0.1:4210/someprefix/config -i -v --basic -u admin:alongpswd
// curl http://127.0.0.1:4210/someprefix/config?format=json -i -v --basic -u admin:alongpswd
// curl http://127.0.0.1:4210/someprefix/config?format=yaml -i -v --basic -u admin:alongpswd
func getConfig(ctx *gin.Context) {
	var req getConfigRequest
	ctx.ShouldBind(&req)
	buf := &bytes.Buffer{}
	if req.Format != "yaml" {
		req.Format = "json"
	}
	gUserData.Write(buf, req.Format)

	//respond
	contentType := "application/json"
	if req.Format == "yaml" {
		contentType = "text/x-yaml"
	}
	ctx.Data(http.StatusOK, contentType, buf.Bytes())
}

// HTTP POST 方式获取所有用户信息。
// Request的Content-Type: "application/x-www-form-urlencoded"
// 请求地址: http://127.0.0.1:4210/someprefix/config
// POST BODY: format=json
//            format=json&another=someothermsg
// 测试:
// curl -X POST http://127.0.0.1:4210/someprefix/config -i -v --basic -u admin:alongpswd
// curl -X POST http://127.0.0.1:4210/someprefix/config -d format=yaml -i -v --basic -u admin:alongpswd
// curl -X POST http://127.0.0.1:4210/someprefix/config -d format=json -i -v --basic -u admin:alongpswd
// curl -X POST http://127.0.0.1:4210/someprefix/config -d format=jsonxxx -i -v --basic -u admin:alongpswd
func postConfig(ctx *gin.Context) {
	format := ctx.PostForm("format")
	if format == "" {
		format = "json"
	} else if format != "json" && format != "yaml" {
		ctx.JSON(http.StatusOK, Response{Code: 1, Msg: "Invalid format parameter"})
		return
	} else {

	}
	buf := &bytes.Buffer{}
	gUserData.Write(buf, format)

	contentType := "application/json"
	if format == "yaml" {
		contentType = "text/x-yaml"
	}
	ctx.Data(http.StatusOK, contentType, buf.Bytes())
}

// 使用HTTP PUT时的Request格式
type AddUserRequest struct {
	Data VIPInfo `json:"data"`
}

// HTTP PUT: 添加新用户
// Request的Content-Type: "application/json"
// 请求地址 HTTP://127.0.0.1:4210/someprefix/config/user
// 测试: python3 test_my_http_api.py -a
func AddUser(ctx *gin.Context) {
	var req AddUserRequest
	ctx.ShouldBindJSON(&req.Data)
	if req.Data.Username != "" {
		bExist := false
		for _, ele := range gUserData.Users {
			if ele.Username == req.Data.Username {
				bExist = true
				break
			}
		}
		if !bExist {
			gUserData.Users = append(gUserData.Users, req.Data)
			ctx.JSON(http.StatusOK, Response{Code: 0, Msg: "OK"})
		} else {
			ctx.JSON(http.StatusOK, Response{Code: 2, Msg: "User already exists"})
		}
	} else {
		ctx.JSON(http.StatusOK, Response{Code: 1, Msg: "User name can not be empty"})
	}
}

type queryUserRequest struct {
	//在URL路径中
	username string `url:"username"`
	//在BODY中
	query string
}

type queryUserResponse struct {
	Code int      `json:"code,omitempty"`
	Data *VIPInfo `json:"data,omitempty"`
}

// HTTP POST: 查询用户信息
// Request的Content-Type: "application/x-www-form-urlencoded"
// 请求地址: http://127.0.0.1:4210/someprefix/config/user/:username
// POST BODY: query=someitem
//            query=someitem
func QueryUser(ctx *gin.Context) {
	var req queryUserRequest
	err := ctx.ShouldBindUri(&req)
	if err != nil {
		ctx.JSON(http.StatusOK, Response{Code: 2, Msg: err.Error()})
		return
	}
	req.query = ctx.PostForm("query")
	if req.username == "" {
		ctx.JSON(http.StatusOK, Response{Code: 1, Msg: "User name can not be empty"})
	} else {
		bExists := false
		for _, user := range gUserData.Users {
			if user.Username == req.username {
				bExists = true
				ctx.JSON(http.StatusOK, queryUserResponse{Code: 0, Data: &user})
				break
			}
		}
		if !bExists {
			ctx.JSON(http.StatusOK, Response{Code: 3, Msg: "User not exists"})
		}
	}
}
